﻿#region

using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Vin.Tool.AspNetCore.Extension;
using Vin.Tool.Attributes;
using Vin.Tool.Core.AssemblyCategory;
using Vin.Tool.Core.AssertCategory;
using Vin.Tool.Enums;

#endregion

namespace Vin.DependencyInjection.Core.Extension;

public static class ServiceExtension
{
    private static void AddService(IServiceCollection serviceCollection, ServiceAttribute serviceAttribute,
        Type implementationType)
    {
        switch (serviceAttribute.Lifetime)
        {
            case Lifetime.Scoped:
                serviceCollection.AddScoped(serviceAttribute.InterfaceType ?? implementationType, implementationType);
                if (serviceAttribute.Key.IsNotNullOrEmpty())
                {
                    // serviceCollection.AddKeyedScoped(serviceAttribute.InterfaceType ?? implementationType,
                    //     serviceAttribute.Key, implementationType);
                    serviceCollection.AddKeyedScoped(serviceAttribute.InterfaceType ?? implementationType,
                        serviceAttribute.Key, implementationType);
                }

                break;
            case Lifetime.Singleton:
                serviceCollection.AddSingleton(serviceAttribute.InterfaceType ?? implementationType,
                    implementationType);
                if (serviceAttribute.Key.IsNotNullOrEmpty())
                {
                    // ServiceCollectionServiceExtensions.AddKeyedSingleton(serviceCollection,
                    //     serviceAttribute.InterfaceType ?? implementationType,
                    //     serviceAttribute.Key, implementationType);
                    serviceCollection.AddKeyedSingleton(serviceAttribute.InterfaceType ?? implementationType,
                        serviceAttribute.Key, implementationType);
                }

                break;
            case Lifetime.Transient:
                serviceCollection.AddTransient(serviceAttribute.InterfaceType ?? implementationType,
                    implementationType);
                if (serviceAttribute.Key.IsNotNullOrEmpty())
                {
                    // ServiceCollectionServiceExtensions.AddKeyedTransient(serviceCollection,
                    //     serviceAttribute.InterfaceType ?? implementationType,
                    //     serviceAttribute.Key, implementationType);
                    serviceCollection.AddKeyedTransient(serviceAttribute.InterfaceType ?? implementationType,
                        serviceAttribute.Key, implementationType);
                }

                break;
        }
    }

    private static void AddService(IServiceCollection serviceCollection, Type serviceType,
        Type implementationType, ServiceAttribute serviceAttribute)
    {
        switch (serviceAttribute.Lifetime)
        {
            case Lifetime.Scoped:
                serviceCollection.AddScoped(serviceType, implementationType);
                if (serviceAttribute.Key.IsNotNullOrEmpty())
                {
                    // ServiceCollectionServiceExtensions.AddKeyedScoped(serviceCollection, serviceType,
                    //     serviceAttribute.Key, implementationType);
                    serviceCollection.AddKeyedScoped(serviceType, serviceAttribute.Key, implementationType);
                }

                break;
            case Lifetime.Singleton:
                serviceCollection.AddSingleton(serviceType, implementationType);
                if (serviceAttribute.Key.IsNotNullOrEmpty())
                {
                    // ServiceCollectionServiceExtensions.AddKeyedSingleton(serviceCollection, serviceType,
                    //     serviceAttribute.Key, implementationType);
                    serviceCollection.AddKeyedSingleton(serviceType, serviceAttribute.Key, implementationType);
                }

                break;
            case Lifetime.Transient:
                serviceCollection.AddTransient(serviceType, implementationType);
                if (serviceAttribute.Key.IsNotNullOrEmpty())
                {
                    // ServiceCollectionServiceExtensions.AddKeyedTransient(serviceCollection, serviceType,
                    //     serviceAttribute.Key, implementationType);
                    serviceCollection.AddKeyedTransient(serviceType, serviceAttribute.Key, implementationType);
                }

                break;
        }
    }

    /// <summary>
    /// 获取程序集中的所有服务
    /// </summary>
    /// <param name="serviceCollection"></param>
    public static void RegisterServices(this IServiceCollection serviceCollection)
    {
        var allTypes = AssemblyTool.GetTypesByAttribute<ServiceAttribute>()
            .Select(m => new
            {
                type = m,
                attr = m.GetCustomAttributes<ServiceAttribute>().FirstOrDefault()!,
            });
        // 排序
        var serviceTypeList = allTypes.OrderBy(m => m.attr.Order);

        foreach (var item in serviceTypeList)
        {
            // 忽略标记了忽略服务的接口
            if (item.type.IsDefined(typeof(IgnoreServiceAttribute), false) || item.type.IsAbstract) continue;

            // 先注入自身
            AddService(serviceCollection, item.attr, item.type);

            // 如果指定了单个接口，则注入指定的接口
            if (item.attr.InterfaceType != null)
            {
                // AddService(serviceCollection, item.attr!, item.type);
                AddService(serviceCollection, item.attr!, item.attr.InterfaceType);
                continue;
            }

            // 如果指定了多个接口，则注入多个接口
            if (item.attr.InterfaceTypes.Length > 0)
            {
                foreach (var interfaceType in item.attr.InterfaceTypes)
                {
                    AddService(serviceCollection, interfaceType, item.type, item.attr);
                }

                continue;
            }

            // 其他是不指定，全部接口注入
            var interfaces = item.type.GetInterfaces();
            foreach (var interfaceType in interfaces)
            {
                // 忽略标记了忽略服务的接口
                if (interfaceType.IsDefined(typeof(IgnoreServiceAttribute))) continue;

                // 如果是泛型
                if (interfaceType.IsGenericType || item.type.IsGenericType)
                {
                    // 且泛型参数个数不一致，类型不一致，则忽略
                    if (!interfaceType.GetGenericArguments().SequenceEqual(item.type.GetGenericArguments())) continue;
                }

                var serviceType = interfaceType;
                if (interfaceType.IsGenericType)
                {
                    // item.attr.InterfaceType = item.type.MakeGenericType(item.attr.InterfaceType.GenericTypeArguments);
                    // item.attr.InterfaceType = item.attr.InterfaceType.GetGenericTypeDefinition();
                    // serviceType = item.type.MakeGenericType(serviceType.GenericTypeArguments);
                    serviceType = interfaceType.GetGenericTypeDefinition();
                }

                AddService(serviceCollection, serviceType, item.type, item.attr);
            }

            #region 可以用，只注入一个接口，容易注入找不到

            // if (item.attr?.InterfaceType == null)
            // {
            //     if (item.attr?.UseLastInterfaceType == true)
            //     {
            //         // var interfaces = item.type.GetInterfaces()
            //         //     .OrderByDescending(i =>
            //         //         item.type.GetInterfaceMap(i).TargetMethods.FirstOrDefault()?.MetadataToken);
            //         var interfaces = item.type.GetInterfaces().OrderBy(m => m.GetInterfaces().Length);
            //         item.attr.InterfaceType = interfaces.LastOrDefault();
            //     }
            // }
            //
            // if (item.attr!.InterfaceType == null)
            // {
            //     item.attr.InterfaceType = item.type;
            // }
            //
            // if (item.attr.InterfaceType.IsGenericType)
            // {
            //     // item.attr.InterfaceType = item.type.MakeGenericType(item.attr.InterfaceType.GenericTypeArguments);
            //     item.attr.InterfaceType = item.attr.InterfaceType.GetGenericTypeDefinition();
            // }
            //
            // AddService(serviceCollection, item.attr!, item.type);

            #endregion
        }
    }
}